/* https://cirosantilli.com/linux-kernel-module-cheat#assembly-registers */

#include <lkmc.h>

LKMC_PROLOGUE

    /* 13 general purpose registers. */
    mov r0, 0
    mov r1, 1
    mov r2, 2
    mov r3, 3
    mov r4, 4
    mov r5, 5
    mov r6, 6
    mov r7, 7
    mov r8, 8
    mov r9, 9
    mov r10, 10
    mov r11, 11
    mov r12, 12

    /* * r11: aliased to FP (frame pointer, debug stack trace usage only)
     * +
     * I think FP is only a convention with no instruction impact, but TODO:
     * not mentioned on AAPCS. aarch64 AAPCS mentions it though.
     * * r13: aliased to SP (stack pointer), what push / pop use
     * * r14: aliased to LR (link register), what bl writes the return address to
     * * r15: aliased to PC (program counter), contains the current instruction address
     *
     * In ARMv8, SP and PC have dedicated registers in addition to
     * the 32-general purpose ones. LR is still general purpose as before.
     *
     * Therefore, it is possible to use those registers in any place
     * other registers may be used.
     *
     * This is not possible in ARMv8 anymore.
     *
     * For example, we can load an address into PC, which is very similar to what B / BX does:
     * https://stackoverflow.com/questions/32304646/arm-assembly-branch-to-address-inside-register-or-memory/54145818#54145818
     */
    ldr pc, =10f
    LKMC_ASSERT_FAIL
10:

    /* Same with r15, which is the same as pc. */
    ldr r15, =10f
    LKMC_ASSERT_FAIL
10:

    /* Another example with mov reading from pc. */
    mov r0, pc
    /* Why sub 8:
     * https://stackoverflow.com/questions/24091566/why-does-the-arm-pc-register-point-to-the-instruction-after-the-next-one-to-be-e
     */
    sub r0, r0, 8

    /* pc-relative load also just work just like any other register. */
    ldr r0, [pc]
    b 1f
    .word 0x12345678
1:
    LKMC_ASSERT_EQ(r0, =0x12345678)

    /* We can also use fp in GNU GAS assembly. */
    mov r11, 0
    mov fp, 1
    LKMC_ASSERT_EQ(r11, =1)
LKMC_EPILOGUE
